AWS Backup からプライベート IP を固定して EC2 を復元する際、jq を使ってお手軽に実行できるようにしてみた

AWS Backup からプライベート IP を固定して EC2 を復元する際、jq を使ってお手軽に実行できるようにしてみた

こんにちは。枡川です。
AWS Backup で EC2 インスタンスを復元する際、システム要件で既存のプライベート IP を引き継ぐ必要がある場合もあると思います。
その際、AWS Backup のサービスページからの復元ではプライベート IP を指定することができません。

スクリーンショット 2025-01-02 18.33.28

スクリーンショット 2025-01-02 18.53.40

スクリーンショット 2025-01-02 18.53.47

AMI ID を直接指定して EC2 のサービスページから復元を行うことは可能ですが、新しく EC2 を新規構築する手順と変わらないため、どうしても設定項目が多くなってしまいます。

スクリーンショット 2025-01-02 18.35.34

そこで、AWS CLI を利用して、プライベート IP を指定しつつ AWS Backup 経由でリストアする方法を試してみました。

https://repost.aws/ja/knowledge-center/backup-change-retain-ip-ec2-restore

また、リストアのために JSON ファイルの準備が必要になるのですが、思ったより手間だったので jq を利用して手順を簡略化してみました。

https://github.com/jqlang/jq

AWS Backup でのプライベート IP を指定したリストアについて

AWS Backup の start-restore-job コマンドを利用してリストアを行います。
この際、--metadata オプションとして JSON ファイルを指定することができ、この中でプライベート IP などの指定を行うことができます。
また、バックアップ取得元インスタンスの情報は get-recovery-point-restore-metadata コマンドで取得できるため、こちらから取得した情報を元に JSON ファイルを生成することが可能です。
プライベート IP を指定して AWS Backup 経由でリストアする理屈はシンプルですが、get-recovery-point-restore-metadata コマンドで取得した情報をそのまま start-restore-job で利用することはできず、それなりに変換が必要です。

get-recovery-point-restore-metadata で情報を取得した後、変換が必要な項目について

変換が必要な内容を確認します。

RestoreMetadata 属性を抜き出す必要がある

get-recovery-point-restore-metadata のレスポンスは下記のようになりますが、RestoreMetadata だけを抜き出す必要があります。

{
  "BackupVaultArn": "arn:aws:backup:ap-northeast-1:xxxxxxxxxxxxx:backup-vault:Default",
  "RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-070805fd4be75c6f3",
  "RestoreMetadata": {
    "Architecture": "x86_64",
    "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
    "CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":2}",
    "CreditSpecification": "{\"CpuCredits\":\"unlimited\"}",
    "DisableApiTermination": "false",
    "EbsOptimized": "false",
    "EnaSupport": "true",
    "HibernationOptions": "{\"Configured\":false}",
    "IamInstanceProfileName": "EcsTransferFamilyStack-BastionBastionbastionhostInstanceProfileA17C8709-GQ3vua4aFyID",
    "InstanceInitiatedShutdownBehavior": "stop",
    "InstanceType": "t3.micro",
    "Monitoring": "{\"State\":\"disabled\"}",
    "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
    "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
    "RequireIMDSv2": "true",
    "RootDeviceType": "ebs",
    "SecurityGroupIds": "[\"sg-09c735afff02550a4\"]",
    "SubnetId": "subnet-0b40ad6d419ddd266",
    "VirtualizationType": "hvm",
    "VpcId": "vpc-05b9ef1203d324989",
    "aws:backup:request-id": "a25f0826-9a59-40d4-aab2-b50575c03517"
  },
  "ResourceType": "EC2"
}

SecurityGroupIds と SubnetId を削除

RestoreMetadata だけを抜き出すと下記のようになります。

{
  "Architecture": "x86_64",
  "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
  "CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":2}",
  "CreditSpecification": "{\"CpuCredits\":\"unlimited\"}",
  "DisableApiTermination": "false",
  "EbsOptimized": "false",
  "EnaSupport": "true",
  "HibernationOptions": "{\"Configured\":false}",
  "IamInstanceProfileName": "EcsTransferFamilyStack-BastionBastionbastionhostInstanceProfileA17C8709-GQ3vua4aFyID",
  "InstanceInitiatedShutdownBehavior": "stop",
  "InstanceType": "t3.micro",
  "Monitoring": "{\"State\":\"disabled\"}",
  "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
  "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
  "RequireIMDSv2": "true",
  "RootDeviceType": "ebs",
  "SecurityGroupIds": "[\"sg-09c735afff02550a4\"]",
  "SubnetId": "subnet-0b40ad6d419ddd266",
  "VirtualizationType": "hvm",
  "VpcId": "vpc-05b9ef1203d324989",
  "aws:backup:request-id": "a25f0826-9a59-40d4-aab2-b50575c03517"
}

この内、SecurityGroupIds と SubnetId は NetworkInterfaces 内に含まれる情報と重複しています。
そのため、属性を削除しないとリストア時に下記のようなエラーが発生します。

SecurityGroupIds が重複している場合

Network interfaces and an instance-level security groups may not be specified on the same request

SubnetId が重複している場合

Network interfaces and an instance-level subnet ID may not be specified on the same request

NetworkInterfaces 属性にある PrivateIpAddress と SecondaryPrivateIpAddressCount を削除

get-recovery-point-restore-metadata のレスポンスでは NetworkInterfaces 属性内に PrivateIpAddress 属性と PrivateIpAddresses 属性が両方存在しています。

"NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",

このまま利用すると下記エラーが発生するので、今回は PrivateIpAddress 属性を削除します。

Only one primary private IP address can be specified.

また、secondaryPrivateIpAddressCount 属性が 0 だと下記エラーになるので、今回は ENI が一つであると想定して削除します。

Value (0) for parameter secondaryPrivateIpAddressCount is invalid. Value must be a positive number.

セカンダリ IP がある場合は削除不要なので、ご注意下さい。

NetworkInterfaces 属性にある AssociatePublicIpAddress を false にする

get-recovery-point-restore-metadata から取得すると、EC2 の状態に関わらず AssociatePublicIpAddress が true になってしまうようです。
この状態だとリストアした EC2 に対してパブリック IP が紐づいてしまうので、false に変更します。

jq を使ってリストア用の JSON ファイルを生成してみる

これまでの手順を踏まえて、jq を利用したコマンドを作成してみました。

aws backup get-recovery-point-restore-metadata \
  --backup-vault-name ${BACKUP_VAULT_NAME} \
  --recovery-point-arn ${RECOVERY_POINT_ARN} \
  | jq '.RestoreMetadata | del(.SubnetId, .SecurityGroupIds) | .NetworkInterfaces |= (fromjson | .[0] |= del(.PrivateIpAddress, .NetworkInterfaceId, .SecondaryPrivateIpAddressCount) | .[0].AssociatePublicIpAddress = false | tojson)' > meta_data.json

生成したメタデータを利用してリストアを実行してみます。

% aws backup start-restore-job \
  --recovery-point-arn ${RECOVERY_POINT_ARN} \
  --iam-role-arn ${BACKUP_ROLE_ARN}  \
  --metadata file://meta_data.json \
  --copy-source-tags-to-restored-resource
{
    "RestoreJobId": "E929C76E-7CCF-979C-55BA-A8F803C4F2A9"
}

無事リストアも成功しました。

% aws backup describe-restore-job --restore-job-id E929C76E-7CCF-979C-55BA-A8F803C4F2A9
{
    "AccountId": "xxxxxxxxxxxx",
    "RestoreJobId": "E929C76E-7CCF-979C-55BA-A8F803C4F2A9",
    "RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-070805fd4be75c6f3",
    "CreationDate": "2025-01-03T01:14:18.361000+09:00",
    "CompletionDate": "2025-01-03T01:15:35.172000+09:00",
    "Status": "COMPLETED",
    "PercentDone": "0.00%",
    "BackupSizeInBytes": 8589934592,
    "IamRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/AWSBackupRoleWithPassRole",
    "CreatedResourceArn": "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-06f933962cd38d33a",
    "ResourceType": "EC2",
    "RecoveryPointCreationDate": "2025-01-02T20:30:00+09:00",
    "CreatedBy": {}
}

まとめ

AWS Backup からプライベート IP を固定して EC2 を復元してみました。
バックアップ元の EC2 で利用していたプライベート IP を使う場合は事前に IP の開放(インスタンスの削除)が必要なのでご注意下さい。
それなりに手間なので、料金が許容できるのであれば NLB で IP 固定できると良いなと思いました。
本ブログがどなたかの参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.